package com.fansl.allround.auth.endpoint;

import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.baidu.unbiz.fluentvalidator.ComplexResult;
import com.baidu.unbiz.fluentvalidator.FluentValidator;
import com.baidu.unbiz.fluentvalidator.ResultCollectors;
import com.fansl.allround.auth.dto.BindSocialUser;
import com.fansl.allround.auth.provider.social.SysSocialUserSignUp;
import com.fansl.allround.auth.provider.social.UcenterSocialUserSignUp;
import com.fansl.allround.common.core.constant.SecurityConstants;
import com.fansl.allround.common.core.constant.enums.*;
import com.fansl.allround.common.core.exception.AllroundBizException;
import com.fansl.allround.common.core.exception.CheckedException;
import com.fansl.allround.common.core.util.R;
import com.fansl.allround.common.core.validator.MobileValidator;
import com.fansl.allround.common.core.validator.NotNullValidator;
import com.fansl.allround.open.api.entity.sms.SendSmsCodePO;
import com.fansl.allround.open.api.feign.RemoteJuHeService;
import com.fansl.allround.open.api.vo.SmsCodeVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.util.Date;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * @author fansl
 * @Description: TODO
 * @date 2019/11/4 13:43
 */
@Slf4j
@RestController
@RequestMapping("/login")
public class LoginController {
    @Autowired
    private RemoteJuHeService remoteJuHeService;
    @Resource
    private RedisTemplate<String, java.io.Serializable> redisTemplate;
    @Autowired
    private SysSocialUserSignUp sysSocialUserSignUp;
    @Autowired
    private UcenterSocialUserSignUp ucenterSocialUserSignUp;

    @GetMapping("/sms")
    public R<?> smsCode(@RequestParam String mobile, @RequestParam ServiceTypeEnum serviceType) {
        ComplexResult result = FluentValidator.checkAll()
                .on(mobile, new MobileValidator())
                .doValidate()
                .result(ResultCollectors.toComplex());
        if (!result.isSuccess()) {
            return new R<>(ResultEnum.FAILED, result.getErrors());
        }
        //验证发送间隔
        String loginSendSmsCodeIntervalRedisKey =
                String.format(SecurityConstants.LOGIN_SEND_SMS_CODE_TIMES_INTERVAL_KEY, serviceType.name(), mobile);
        if (Boolean.TRUE.equals(redisTemplate.hasKey(loginSendSmsCodeIntervalRedisKey))) {
            Long expire = redisTemplate.getExpire(loginSendSmsCodeIntervalRedisKey, TimeUnit.SECONDS);
            throw new CheckedException("验证码发送太频繁，请" + expire.toString() + "秒后再试。");
        }
        //验证发送次数
        String loginSendSmsCodeRedisKey =
                String.format(SecurityConstants.LOGIN_SEND_SMS_CODE_TIMES_KEY, serviceType.name(), mobile);
        Object loginSendSmsCodeTimesObj = redisTemplate.opsForValue().get(loginSendSmsCodeRedisKey);
        if (null != loginSendSmsCodeTimesObj) {
            //判断发送次数是否超过最大次数限制
            if ((int) loginSendSmsCodeTimesObj >= LoginSmsTimeIntervalEnum.MAX.getTimes()) {
                throw new CheckedException("验证码发送超过最大次数。");
            }
        }
        SendSmsCodePO sendSmsCodePO = new SendSmsCodePO();
        sendSmsCodePO.setMobile(mobile);
        sendSmsCodePO.setSmsCodeType(SmsCodeTypeEnum.LOGIN);
        SmsCodeVO smsCodeVO = remoteJuHeService.smsCode(sendSmsCodePO);
        if (StrUtil.isBlank(smsCodeVO.getCode())) {
            log.error("desc:{},result:{}", "登录发送手机验证码[异常]", JSONObject.toJSONString(smsCodeVO));
            throw new AllroundBizException("服务异常");
        }
        log.info("desc:{},result:{}", "登录发送手机验证码", JSONObject.toJSONString(smsCodeVO));
        //验证码存在redis
        String loginSmsCodeRedisKey =
                String.format(SecurityConstants.LOGIN_SMS_CODE_KEY, serviceType.name(), mobile);
        redisTemplate.opsForValue().set(loginSmsCodeRedisKey, smsCodeVO.getCode(), 15, TimeUnit.MINUTES);
        //验证码发送次数存在redis，设置时间间隔
        long tm = DateUtil.between(new Date(), DateUtil.beginOfDay(DateUtil.tomorrow()), DateUnit.MS);
        if (null == loginSendSmsCodeTimesObj) {

            redisTemplate.opsForValue().set(loginSendSmsCodeRedisKey, 1, tm, TimeUnit.MILLISECONDS);

            //设置时间间隔
            LoginSmsTimeIntervalEnum timeInterval = LoginSmsTimeIntervalEnum.getByTimes(1);
            redisTemplate.opsForValue().set(loginSendSmsCodeIntervalRedisKey, 1,
                    timeInterval.getInterval(), timeInterval.getTimeUnit());
        } else {
            Long times = redisTemplate.opsForValue().increment(loginSendSmsCodeRedisKey, 1L);
            //设置时间间隔
            LoginSmsTimeIntervalEnum timeInterval = LoginSmsTimeIntervalEnum.getByTimes(times.intValue());
            redisTemplate.opsForValue().set(loginSendSmsCodeIntervalRedisKey, 1,
                    timeInterval.getInterval(), timeInterval.getTimeUnit());
        }
        return new R<>(Boolean.TRUE);
    }

    /**
     * 登录绑定用户手机号发送验证码
     *
     * @param mobile
     * @param serviceType
     * @return
     */
    @GetMapping("/bind/sms")
    public R bindSmsCode(@RequestParam String mobile, @RequestParam ServiceTypeEnum serviceType) {
        if (log.isDebugEnabled()) {
            log.debug("登录绑定用户手机号发送验证码,mobile:{},serviceType:{}", mobile, serviceType);
        }
        ComplexResult result = FluentValidator.checkAll()
                .on(mobile, new MobileValidator())
                .doValidate()
                .result(ResultCollectors.toComplex());
        if (!result.isSuccess()) {
            return new R(ResultEnum.FAILED, result.getErrors());
        }
        //验证发送间隔
        String loginBindSmsCodeIntervalRedisKey =
                String.format(SecurityConstants.LOGIN_SEND_BIND_SMS_CODE_TIMES_INTERVAL_KEY, serviceType.name(), mobile);
        if (redisTemplate.hasKey(loginBindSmsCodeIntervalRedisKey)) {
            Long expire = redisTemplate.getExpire(loginBindSmsCodeIntervalRedisKey, TimeUnit.SECONDS);
            throw new CheckedException("验证码发送太频繁，请" + expire.toString() + "秒后再试。");
        }
        SendSmsCodePO sendSmsCodePO = new SendSmsCodePO();
        sendSmsCodePO.setMobile(mobile);
        sendSmsCodePO.setSmsCodeType(SmsCodeTypeEnum.LOGIN_BIND);
        SmsCodeVO smsCodeVO = remoteJuHeService.smsCode(sendSmsCodePO);
        if (StrUtil.isBlank(smsCodeVO.getCode())) {
            log.error("desc:{},result:{}", "登录绑定用户信息发送手机验证码[异常]", JSONObject.toJSONString(smsCodeVO));
            throw new AllroundBizException("服务异常");
        }
        log.info("desc:{},result:{}", "登录绑定用户信息发送手机验证码", JSONObject.toJSONString(smsCodeVO));
        //验证码存在redis
        String loginBindSmsCodeRedisKey =
                String.format(SecurityConstants.LOGIN_BIND_SMS_CODE_KEY, serviceType.name(), mobile);
        redisTemplate.opsForValue().set(loginBindSmsCodeRedisKey, smsCodeVO.getCode(), 15, TimeUnit.MINUTES);
        //验证码发送频率设置时间间隔
        redisTemplate.opsForValue().set(loginBindSmsCodeIntervalRedisKey, 1,
                1, TimeUnit.MINUTES);
        return new R<>(Boolean.TRUE);
    }

    /**
     * 查询是否存在第三方登录的用户
     *
     * @param oauthCode
     * @param code
     * @return
     */
    @GetMapping("/check")
    public R<com.fansl.allround.auth.dto.ExistSocialUser> existUserByOpenid(@RequestParam Integer oauthCode,
                                                                            @RequestParam String code,
                                                                            @RequestParam ServiceTypeEnum serviceType) {
        OauthTypeEnum oauthType = OauthTypeEnum.getByCode(oauthCode);
        switch (serviceType) {
            case BACKEND:
                return new R<>(sysSocialUserSignUp.existUser(oauthType, code));
            case UCENTER:
                return new R<>(ucenterSocialUserSignUp.existUser(oauthType, code));
            default:
                throw new AllroundBizException("不支持的服务类型");
        }
    }

    @PostMapping("/user/bind")
    public R<?> bindSocialUser(@RequestParam ServiceTypeEnum serviceType,
                            @RequestParam Integer oauthCode,
                            @RequestBody BindSocialUser param) {
        ComplexResult result = FluentValidator.checkAll()
                .on(param.getCode(), new NotNullValidator("参数code"))
                .on(param.getMobile(), new NotNullValidator("手机号码"))
                .on(param.getMobileCode(), new NotNullValidator("手机验证码"))
                .doValidate()
                .result(ResultCollectors.toComplex());
        if (!result.isSuccess()) {
            return new R<>(ResultEnum.FAILED, result.getErrors());
        }
        //校验手机验证码
        String loginBindSmsCodeRedisKey =
                String.format(SecurityConstants.LOGIN_BIND_SMS_CODE_KEY, serviceType.name(), param.getMobile());
        Object loginBindSmsCode = redisTemplate.opsForValue().get(loginBindSmsCodeRedisKey);
        boolean validSmsCode = (Objects.nonNull(loginBindSmsCode) && loginBindSmsCode.equals(param.getMobileCode()));
        if (!validSmsCode) {
            throw new CheckedException("手机验证码不正确或失效");
        }
        switch (serviceType) {
            case UCENTER:
                break;
            case BACKEND: {
                param.setOauthType(OauthTypeEnum.getByCode(oauthCode));
                param.setServiceType(serviceType);
                return new R<>(sysSocialUserSignUp.bindSocialUser(param));
            }
            default:
                throw new AllroundBizException("不支持的服务类型");
        }
        return new R<>(Boolean.FALSE);
    }
}
